function [RecoveredResult] = Ddavid_kNN_recover_multi_label_ver2(SampledLabel, Option, KNNList, TrueLabel)

% [RecoveredResult] = Ddavid_kNN_recover_multi_label_ver2(SampledLabel, E, C, T, K, KNNList)
%
% <Input>
% SampledLabel: [n*m], the value is {-1, 1}, the sampled labels of all
%               instances, where n is the number of instances, m is the
%               number of labels, -1 means unknown and 1 means the
%               instance has the label
% Option: structure, includes the options required of this algorithm
%   E: double, the range is (0.5, 1), the threshold
%   C: double, the range is (0, 0.5), the updating range
%   T: the max number of iterations
%   K: the K value of KNN
%   CombineLabel: set to ture to combine the original labels
% KNNList: The KNN List (for saving time)
% TrueLabel: [n*m], the value is {-1, 0, 1}, the true labels of all
%            instancess
%
% <Output>
% RecoveredResult: structure
%   RecoveredLabel: [n*m], the value is {-1, 0, 1}, all recovered labels
%                   of all instances
%   kNNResult: [n*m], the value is [-1, 1], the scores of all instances
%   Weight: [n*m], the value is [-1. 1], the final weights of all instances
%   OriHammingLoss: double, the hamming loss of the SampledLabel
%   HammingLoss: double, the hamming loss of the RecoveredLabel
%   CondInfo1: struct, the information of the triggered times of each
%             condition in the 1st stage
%     HighConfidence: [T*m]
%     UnimportantTrueLabel: [T*m]
%     Normal: [T*m]
%     ProcessedTable: [n*T*m]
%     kNNResultTable: [n*(T+1)*m]
%     ConfusionMatrix: [4*m]
%     HammingLoss: double

N = size(SampledLabel, 1);
M = size(SampledLabel, 2);
RecoveredResult.RecoveredLabel = zeros(N, M);

RecoveredResult.SingleLabelHammingLoss = zeros(1, M);
RecoveredResult.OriSingleLabelHammingLoss = zeros(1, M);
RecoveredResult.kNNResult = zeros(N, M);
RecoveredResult.Weight = zeros(N, M);
RecoveredResult.CondInfo1.HighConfidence = zeros(Option.T, M);
RecoveredResult.CondInfo1.UnimportantTrueLabel = zeros(Option.T, M);
RecoveredResult.CondInfo1.Normal = zeros(Option.T, M);
RecoveredResult.CondInfo1.ProcessedTable = zeros(N, Option.T, M);
RecoveredResult.CondInfo1.kNNResultTable = zeros(N, Option.T + 1, M);

% 1st stage: basic recovering
for i = 1:M
    Label = SampledLabel(:, i);
    Label(Label == -1) = 0;

    [RecoveredResult.RecoveredLabel(:, i), RecoveredResult.kNNResult(:, i), RecoveredResult.Weight(:, i), CondInfo] = Ddavid_kNN_recover_binary(Label, Option, KNNList);
   
    RecoveredResult.CondInfo1.HighConfidence(:, i) = CondInfo.HighConfidence;
    RecoveredResult.CondInfo1.UnimportantTrueLabel(:, i) = CondInfo.UnimportantTrueLabel;
    RecoveredResult.CondInfo1.Normal(:, i) = CondInfo.Normal;
    RecoveredResult.CondInfo1.ProcessedTable(:, :, i) = CondInfo.ProcessedTable;
    RecoveredResult.CondInfo1.kNNResultTable(:, :, i) = CondInfo.kNNResultTable;
    
    RecoveredResult.RecoveredLabel(RecoveredResult.RecoveredLabel(:, i) > 0, i) = 1;
    RecoveredResult.RecoveredLabel(RecoveredResult.RecoveredLabel(:, i) <= 0, i) = -1;
    RecoveredResult.SingleLabelHammingLoss(i) = Hamming_loss(RecoveredResult.RecoveredLabel(:, i)', TrueLabel(:, i)');
    RecoveredResult.OriSingleLabelHammingLoss(i) = Hamming_loss(SampledLabel(:, i)', TrueLabel(:, i)');
end
RecoveredResult.CondInfo1.RecoveredLabel = RecoveredResult.RecoveredLabel;

% Calculate the missing of all labels
RecoveredResult.CondInfo1.PreLabelSize = sum(RecoveredResult.CondInfo1.RecoveredLabel == 1, 1);
RecoveredResult.CondInfo1.SampledLabelSize = sum(SampledLabel == 1, 1);
RecoveredResult.CondInfo1.Missing = sum((TrueLabel == 1) & (SampledLabel == 1) & (RecoveredResult.CondInfo1.RecoveredLabel == -1), 1);

% Output information
RecoveredResult.CondInfo1.HammingLoss = Hamming_loss(RecoveredResult.CondInfo1.RecoveredLabel', TrueLabel');

% 2nd stage: according to the missing to improve the result
for i = 1:M
    if(RecoveredResult.CondInfo1.Missing(i) == RecoveredResult.CondInfo1.SampledLabelSize(i))
        % Use only sampled training labels
        RecoveredResult.RecoveredLabel(:, i) = SampledLabel(:, i);
    elseif(RecoveredResult.CondInfo1.Missing(i) > RecoveredResult.CondInfo1.SampledLabelSize(i) * 0.5)
        % Choose only top-k according to the missing rate
        [~, SortIndex] = sort(RecoveredResult.kNNResult(:, i), 'descend');
        MissingRate = RecoveredResult.CondInfo1.Missing(i) / RecoveredResult.CondInfo1.SampledLabelSize(i);
        TopK = floor(RecoveredResult.CondInfo1.PreLabelSize(i) * (1 - MissingRate));
        RecoveredResult.RecoveredLabel(:, i) = -1;
        RecoveredResult.RecoveredLabel(SortIndex(1:TopK), i) = 1;
        
        % Combine the sampled training labels
        RecoveredResult.RecoveredLabel(SampledLabel(:, i) == 1, i) = 1;
    else
        % Combine the sampled training labels
        RecoveredResult.RecoveredLabel(SampledLabel(:, i) == 1, i) = 1;
    end
end

% Output information
RecoveredResult.OriHammingLoss = Hamming_loss(SampledLabel', TrueLabel');
RecoveredResult.HammingLoss = Hamming_loss(RecoveredResult.RecoveredLabel', TrueLabel');
